#include "log.h"

QMutex Log::mutex;
Log* Log::instance = NULL;
QReadWriteLock Log::rwlock;
QList<QString*> Log::list;

Log::Log(QString fileName, QString filePrefix){
    qDebug()<<"Log::Log thread ID:"<<QThread::currentThreadId();

    logFile = nullptr;// 不手动初始化的话可能不为null
    list.clear();

    newFile(fileName, filePrefix);

    running = true;
    this->start();
}

Log::~Log()
{
    qDebug() << "File:" <<  __FILE__ << " Line:"<<  __LINE__ << " Func:" <<  __FUNCTION__;
    closeFile();
}

void Log::closeFile()
{
    mutexFile.lock();
    if(logFile != nullptr){
        logFile->flush();
        logFile->close();
        delete logFile;
        logFile = nullptr;
    }
    mutexFile.unlock();
}

void Log::createFile(QString filePath)
{
    mutexFile.lock();
    int pos = filePath.lastIndexOf('/');
    QString dirPath;
    QString fileName;
    if(pos != -1){
        dirPath = filePath.left(pos);
        fileName = filePath.right(filePath.size()-pos-1);
    }else{
        dirPath = "";
        fileName = filePath;
    }
    qDebug() << "log dir:" << dirPath << "log file:" << fileName;

    QDir tempDir;
    // save dir of executable file
    QString currentDir = tempDir.currentPath();
    QFile *tempFile = new QFile;
    if(!dirPath.isEmpty()){
        // create dir if it does not exist
        if(!tempDir.exists(dirPath))
        {
            qDebug() << "dir not exist:" << dirPath;
            tempDir.mkpath(dirPath);
        }

        // change dir
        tempDir.setCurrent(dirPath);
    }

    if(tempFile->exists(fileName))
    {
        delete tempFile;
        qDebug() << "file exist:" << fileName;
        mutexFile.unlock();
        return;
    }
    // create file now
    tempFile->setFileName(fileName);
    if(!tempFile->open(QIODevice::WriteOnly|QIODevice::Text))
    {
        qDebug() << "open file failed:" << fileName;
    }
    tempFile->close();

    // change to origin path, this is necessary!!!
    tempDir.setCurrent(currentDir);

    mutexFile.unlock();
}

void Log::newFile(QString fileName, QString filePrefix)
{
    closeFile();
    QString file;
    if(fileName.isEmpty()){
        file = QString("%1.log").arg(QDateTime::currentDateTime().toString("yyyy.MM.dd-hh.mm.ss"));
    }else {
        file = fileName;
    }
    if(!filePrefix.isEmpty()){
        file.prepend(filePrefix);
    }

    qDebug() << "create new log file:" << file;
    createFile(file);

    mutexFile.lock();
    logFile = new QFile(file);

    if(!logFile->open(QIODevice::WriteOnly | QIODevice::Text))
    {
        qDebug() << "open log file failed:" << file;
        delete logFile;
        logFile = nullptr;
        mutexFile.unlock();
        return;
    }else{
        qDebug() << "open log file success:" << file;
    }
    mutexFile.unlock();
}

void Log::run(){
    unsigned int cycles = 0;
    quint64 writeSize = 0;
    qDebug()<<"Log::run thread ID:"<<QThread::currentThreadId();

    while(1)
    {
        rwlock.lockForRead();
        // wait for log data
        while(!list.size()){
            if(!running)
                break;
            rwlock.unlock();
            QThread::msleep(100);
            rwlock.lockForRead();
        }
        rwlock.unlock();
        rwlock.lockForWrite();

        while (list.size())
        {
            // get first string from list
            QString * string = list.front();
            // remove it from list
            list.removeFirst();
            // release the list, so UI thread can use it
            rwlock.unlock();

            // write string to file
            mutexFile.lock();
            if(logFile != nullptr){
                writeSize += logFile->write(string->toLocal8Bit().data(), string->toLocal8Bit().length());
                logFile->flush();
            }else{
                qDebug() << "log file is null, will not write to file";
            }
            mutexFile.unlock();

            // release memory
            delete string;

            // lock for next loop
            rwlock.lockForWrite();
        }
        rwlock.unlock();
        if(!running)
            break;

        cycles++;
        if (cycles % 100 == 0)
            qDebug() << "total write size:" << writeSize;
        QThread::msleep(100);
    }
}

void Log::log(LogLevel level, const char * file,const char * function, int line,const char* data, ...)
{
    if(instance == NULL){
        qDebug() << "get log instance now";
        getInstance();
    }
    QString *string = new QString;
    string->clear();

    switch (level) {
    case Verbose:
        *string += "V";
        break;
    case Info:
        *string += "I";
        break;
    case Debug:
        *string += "D";
        break;
    case Warning:
        *string += "W";
        break;
    case Error:
        *string += "E";
        break;
    case Fatal:
        *string += "F";
        break;
    }
    *string += QString("[%1]").arg(QDateTime::currentDateTime().toString("hh:mm:ss:zzz"));

    char *fileName;
#ifdef Q_OS_WIN
    fileName = strrchr(file, '\\');
#else
    fileName = strrchr(file, '/');
#endif
    if(fileName != nullptr){
        fileName++;
    }else{
        fileName = (char*)file;
    }
    string->append(QString("[%1:%2:%3]").arg(fileName, function).arg(line));

    va_list vlist;
    va_start(vlist,data);
    QByteArray byteArray;
#if defined(Q_OS_WIN)
    int recordLen = _vscprintf(data,vlist);
    byteArray.resize(recordLen);
#else
    byteArray.resize(1024);
#endif
    vsprintf(byteArray.data(),data,vlist);
    string->append(byteArray);
    va_end(vlist);

    string->append("\n");
//    qDebug() << *string;

    rwlock.lockForWrite();
    list.append(string);
    rwlock.unlock();
}
